package com.mm.cloud.lease.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mm.cloud.common.core.result.Result;
import com.mm.cloud.common.core.result.ResultUtil;
import com.mm.cloud.common.security.helper.LoginUserHelper;
import com.mm.cloud.common.security.loginuser.LoginUser;
import com.mm.cloud.lease.dto.income.form.GenerateTenantIncomeDTO;
import com.mm.cloud.lease.entity.TenantIncome;
import com.mm.cloud.lease.entity.TenantSubIncome;
import com.mm.cloud.lease.mapper.TenantIncomeMapper;
import com.mm.cloud.lease.mapper.TenantOtherFeeMapper;
import com.mm.cloud.lease.mapper.TenantSubIncomeMapper;
import com.mm.cloud.lease.mapper.TenantMapper;
import com.mm.cloud.lease.service.ITenantIncomeService;
import com.mm.cloud.lease.mapper.TenantOtherDepositMapper;
import com.mm.cloud.lease.service.impl.convert.income.TenantIncomeConvert;
import com.mm.cloud.lease.vo.income.preview.TenantIncomePreviewVO;
import com.mm.cloud.lease.vo.income.preview.TenantIncomePreviewPeriodVO;
import com.mm.cloud.lease.vo.income.preview.TenantSubIncomePreviewVO;
import com.mm.cloud.lease.dto.tenant.form.item.TenantOtherFeeItemDTO;
import com.mm.cloud.lease.dto.tenant.form.item.TenantOtherDepositItemDTO;
import org.springframework.stereotype.Service;

import jakarta.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;

/**
 * 租客账单 服务实现类
 */
@Service
public class TenantIncomeServiceImpl extends ServiceImpl<TenantIncomeMapper, TenantIncome> implements ITenantIncomeService {

    @Resource
    private TenantMapper tenantMapper;

    @Resource
    private TenantSubIncomeMapper tenantSubIncomeMapper;

    @Resource
    private TenantOtherFeeMapper tenantOtherFeeMapper;

    @Resource
    private TenantOtherDepositMapper tenantOtherDepositMapper;

    /**
     * 生成账单与子账单
     * 说明：
     * - 根据租客资料与入参，计算租赁周期；按缴费方式拆分周期，逐期生成主账单与子账单。
     * - 不使用 BeanUtils，显式逐字段赋值；关键步骤均有注释。
     */
    @Override
    public Result<TenantIncomePreviewVO> previewIncome(GenerateTenantIncomeDTO dto) {
        LoginUser loginUser = LoginUserHelper.getLoginUser();

        // 1. 预览场景：仅使用前端入参，不查询数据库租客资料
        LocalDateTime leaseStart = dto.getLeaseStartTime();
        LocalDateTime leaseEnd = dto.getLeaseEndTime();
        Byte paymentMethod = dto.getPaymentMethod();
        BigDecimal monthlyRent = dto.getLeasePrice();
        Integer advanceDays = dto.getPaymentAdvanceDay();

        if (leaseStart == null || leaseEnd == null || paymentMethod == null || monthlyRent == null || advanceDays == null) {
            return ResultUtil.fail("租期开始/结束、缴费方式、租金、提前付租天数不能为空");
        }

        // 3. 计算周期月数：99 表示全额一次
        int cycleMonths = resolveCycleMonths(paymentMethod, leaseStart, leaseEnd);

        // 2.1 其他费用、其他押金、房租押金全部来源于入参
        List<TenantOtherFeeItemDTO> otherFeeItems = dto.getOtherFeeList();
        List<TenantOtherDepositItemDTO> otherDepositItems = dto.getOtherDepositList();
        BigDecimal roomDeposit = dto.getDepositPrice();

        // 5. 按周期生成
        LocalDateTime cursor = leaseStart;
        long generatedCount = 0;
        int paySeq = 1;
        TenantIncomePreviewVO preview = new TenantIncomePreviewVO();
        List<TenantIncomePreviewPeriodVO> periodItems = new ArrayList<>();
        BigDecimal totalAll = BigDecimal.ZERO;

        while (!cursor.isAfter(leaseEnd.minusSeconds(1))) {
            LocalDateTime periodEnd = computePeriodEnd(cursor, leaseEnd, cycleMonths);

            // 5.1 计算当期主账单应收金额：
            // 假设 leasePrice 为“月租单价”，当期租金 = 月租 * 当期覆盖的月份数
            int monthsInThisPeriod = (int) Math.max(1, ChronoUnit.MONTHS.between(cursor.withDayOfMonth(1), periodEnd.withDayOfMonth(1)) + 1);
            BigDecimal rentThisPeriod = monthlyRent.multiply(BigDecimal.valueOf(monthsInThisPeriod));

            BigDecimal totalThisPeriod = rentThisPeriod;

            // 5.2 计算主账单日期
            LocalDateTime payDate = periodEnd.minusDays(advanceDays);
            if (payDate.isBefore(cursor)) {
                payDate = cursor; // 到期前N天早于开始日时，以开始日为准
            }
            // 子项集合（预览用）
            List<TenantSubIncome> subItems = new ArrayList<>();

            // 5.3 房租子账单（类型 1）
            TenantSubIncome rentSub = TenantIncomeConvert.buildRentSub(
                    loginUser, null, null, null,
                    cursor, periodEnd, paySeq, payDate, rentThisPeriod
            );
            subItems.add(rentSub);

            // 5.4 其他费用子账单（类型 2）
            if (otherFeeItems != null) {
                for (TenantOtherFeeItemDTO fee : otherFeeItems) {
                    if (fee.getFeeMethod() != null && fee.getFeeMethod() == 1) {
                    // 随房租每期收取
                    TenantSubIncome sub = TenantIncomeConvert.buildOtherFeeSub(
                            loginUser, null, null, null,
                            cursor, periodEnd, paySeq, payDate, fee.getDicId(), fee.getFeePrice());
                    subItems.add(sub);
                    totalThisPeriod = totalThisPeriod.add(fee.getFeePrice());
                    } else if (fee.getFeeMethod() != null && fee.getFeeMethod() == 2 && generatedCount == 0) {
                    // 一次性支付，只在第一期生成
                    TenantSubIncome sub = TenantIncomeConvert.buildOtherFeeSub(
                            loginUser, null, null, null,
                            cursor, periodEnd, paySeq, payDate, fee.getDicId(), fee.getFeePrice());
                    subItems.add(sub);
                    totalThisPeriod = totalThisPeriod.add(fee.getFeePrice());
                    }
                }
            }

            // 5.5 押金子账单（类型 0），仅第一期生成
            if (generatedCount == 0 && roomDeposit != null && roomDeposit.compareTo(BigDecimal.ZERO) > 0) {
                TenantSubIncome depositSub = TenantIncomeConvert.buildDepositSub(
                        loginUser, null, null, null,
                        cursor, periodEnd, paySeq, payDate, roomDeposit);
                subItems.add(depositSub);
                totalThisPeriod = totalThisPeriod.add(roomDeposit);
            }

            // 5.6 其他押金子账单（类型 3），仅第一期生成
            if (generatedCount == 0 && otherDepositItems != null) {
                for (TenantOtherDepositItemDTO od : otherDepositItems) {
                    if (od.getDepositPrice() != null && od.getDepositPrice().compareTo(BigDecimal.ZERO) > 0) {
                        TenantSubIncome otherDepositSub = TenantIncomeConvert.buildOtherDepositSub(
                                loginUser, null, null, null,
                                cursor, periodEnd, paySeq, payDate, od.getDicId(), od.getDepositPrice());
                        subItems.add(otherDepositSub);
                        totalThisPeriod = totalThisPeriod.add(od.getDepositPrice());
                    }
                }
            }

            // 组装预览 PeriodItem（主账单结构对齐 TenantIncome）
            TenantIncomePreviewPeriodVO pi = new TenantIncomePreviewPeriodVO();
            pi.setPayCount(paySeq);
            pi.setValidYear(0);
            pi.setValidMonth(cycleMonths == Integer.MAX_VALUE ? null : cycleMonths);
            pi.setValidDay(0);
            pi.setStartValidDate(cursor);
            pi.setEndValidDate(periodEnd);
            pi.setPayDate(payDate);
            pi.setPayMoney(totalThisPeriod);
            pi.setDebtMoney(totalThisPeriod);
            pi.setPayStatus((byte) 1);
            pi.setFinanceMoney(BigDecimal.ZERO);
            pi.setUnfinanceMoney(totalThisPeriod);
            pi.setLateFeeMoney(BigDecimal.ZERO);
            pi.setBreakLateFeeMoney(BigDecimal.ZERO);
            pi.setIsBadDebt((byte) 1);
            pi.setIsSendSms((byte) 1);
            pi.setCreaterId(loginUser.getUserId());
            pi.setCreateTime(LocalDateTime.now());
            pi.setIsDelete((byte) 0);

            List<TenantSubIncomePreviewVO> siList = new ArrayList<>();
            for (TenantSubIncome si : subItems) {
                TenantSubIncomePreviewVO vi = new TenantSubIncomePreviewVO();
                vi.setSubIncomeType(si.getSubIncomeType());
                vi.setSubIncomeDicId(si.getSubIncomeDicId());
                vi.setValidYear(0);
                vi.setValidMonth(cycleMonths == Integer.MAX_VALUE ? null : cycleMonths);
                vi.setValidDay(0);
                vi.setStartValidDate(si.getStartValidDate());
                vi.setEndValidDate(si.getEndValidDate());
                vi.setPayCount(si.getPayCount());
                vi.setPayDate(si.getPayDate());
                vi.setPayMoney(si.getPayMoney());
                vi.setDebtMoney(si.getDebtMoney());
                vi.setPayStatus(si.getPayStatus());
                vi.setFinanceMoney(si.getFinanceMoney());
                vi.setUnfinanceMoney(si.getUnfinanceMoney());
                vi.setLateFeeMoney(si.getLateFeeMoney());
                vi.setBreakLateFeeMoney(si.getBreakLateFeeMoney());
                vi.setIsBadDebt(si.getIsBadDebt());
                vi.setCreaterId(loginUser.getUserId());
                vi.setCreateTime(LocalDateTime.now());
                vi.setIsDelete((byte) 0);
                siList.add(vi);
            }
            pi.setSubItems(siList);
            periodItems.add(pi);
            totalAll = totalAll.add(totalThisPeriod);

            generatedCount++;
            paySeq++;

            if (cycleMonths == Integer.MAX_VALUE) {
                break; // 全额一次
            }
            cursor = periodEnd.plusSeconds(1);
        }

        preview.setTotalAmount(totalAll);
        preview.setTotalPeriods(periodItems.size());
        preview.setPeriods(periodItems);
        return ResultUtil.success(preview);
    }

    /**
     * 解析每期月数。99 表示一次性覆盖完整租期。
     */
    private int resolveCycleMonths(Byte paymentMethod, LocalDateTime start, LocalDateTime end) {
        if (paymentMethod == 99) {
            return Integer.MAX_VALUE;
        }
        return switch (paymentMethod) {
            case 1 -> 1;
            case 2 -> 2;
            case 3 -> 3;
            case 6 -> 6;
            case 12 -> 12;
            default -> 1;
        };
    }

    /**
     * 计算账单期末日（包含端点的自然周期），不超过租期结束。
     */
    private LocalDateTime computePeriodEnd(LocalDateTime periodStart, LocalDateTime leaseEnd, int cycleMonths) {
        if (cycleMonths == Integer.MAX_VALUE) {
            return leaseEnd;
        }
        LocalDateTime candidate = periodStart.plusMonths(cycleMonths).minusSeconds(1);
        if (candidate.isAfter(leaseEnd)) {
            candidate = leaseEnd;
        }
        return candidate;
    }
    
}
